package cn.yuyiling.jelly.kg.service.impl;

import cn.yuyiling.jelly.kg.api.KnowledgePointService;
import cn.yuyiling.jelly.kg.mongodb.entity.KnowledgePoint;
import cn.yuyiling.jelly.kg.mongodb.repository.KnowledgePointRepository;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@DubboService
public class KnowledgePointServiceImpl implements KnowledgePointService {
    private static final Logger logger = LoggerFactory.getLogger(KnowledgePointServiceImpl.class);


    private static final String KEY_PREFIX = "hot:knowledge:";
    private static final String SUMMARY_KEY = "hot:knowledge:top24h";

    @Autowired
    private KnowledgePointRepository knowledgePointRepository;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public KnowledgePoint createKnowledgePoint(KnowledgePoint knowledgePoint) {
        logger.info("Creating knowledge point: {}", knowledgePoint);
        return knowledgePointRepository.save(knowledgePoint);
    }

    @Override
    public KnowledgePoint getKnowledgePointById(String id) {
        logger.info("Getting knowledge point by ID: {}", id);
        Optional<KnowledgePoint> optionalKnowledgePoint = knowledgePointRepository.findById(id);
        return optionalKnowledgePoint.orElse(null);
    }

    @Override
    public List<KnowledgePoint> getAllKnowledgePoints() {
        logger.info("Getting all knowledge points");
        return knowledgePointRepository.findAll();
    }

    @Override
    public KnowledgePoint getKnowledgePointByName(String name) {
        logger.info("Getting knowledge point by name: {}", name);
        return knowledgePointRepository.findByName(name);
    }

    @Override
    public Page<KnowledgePoint> getKnowledgePointsByPage(Pageable pageable) {
        logger.info("Getting knowledge points by page: {}", pageable);
        return knowledgePointRepository.findAll(pageable);
    }

    @Override
    public KnowledgePoint updateKnowledgePoint(String id, KnowledgePoint updatedKnowledgePoint) {
        logger.info("Updating knowledge point with ID: {}, new data: {}", id, updatedKnowledgePoint);
        return knowledgePointRepository.findById(id)
                .map(knowledgePoint -> {
                    knowledgePoint.setName(updatedKnowledgePoint.getName());
                    knowledgePoint.setContent(updatedKnowledgePoint.getContent());
                    if (updatedKnowledgePoint.getMetadata() != null) {
                        knowledgePoint.getMetadata().setTags(updatedKnowledgePoint.getMetadata().getTags());
                    }
                    knowledgePoint.setCategory(updatedKnowledgePoint.getCategory());
                    knowledgePoint.setUpdatedAt(new Date());
                    knowledgePoint.setUpdatedBy(updatedKnowledgePoint.getUpdatedBy());
                    return knowledgePointRepository.save(knowledgePoint);
                })
                .orElseThrow(() -> new RuntimeException("KnowledgePoint not found"));
    }

    @Override
    public void deleteKnowledgePoint(String id) {
        logger.info("Deleting knowledge point with ID: {}", id);
        knowledgePointRepository.deleteById(id);
    }

    @Override
    public void addTagsToKnowledgePoint(String id, List<String> tags) {
        logger.info("Adding tags to knowledge point with ID: {}, tags: {}", id, tags);
        Optional<KnowledgePoint> optionalKnowledgePoint = knowledgePointRepository.findById(id);
        if (optionalKnowledgePoint.isPresent()) {
            KnowledgePoint knowledgePoint = optionalKnowledgePoint.get();
            knowledgePoint.getMetadata().getTags().addAll(tags);
            knowledgePointRepository.save(knowledgePoint);
        }
    }

    @Override
    public void setCategoryForKnowledgePoint(String id, String category) {
        logger.info("Setting category for knowledge point with ID: {}, category: {}", id, category);
        Optional<KnowledgePoint> optionalKnowledgePoint = knowledgePointRepository.findById(id);
        if (optionalKnowledgePoint.isPresent()) {
            KnowledgePoint knowledgePoint = optionalKnowledgePoint.get();
            knowledgePoint.setCategory(category);
            knowledgePointRepository.save(knowledgePoint);
        }
    }

    @Override
    public List<KnowledgePoint> getKnowledgePointsByTags(List<String> tags) {
        logger.info("Getting knowledge points by tags: {}", tags);
        return knowledgePointRepository.findByTagsIn(tags);
    }

    @Override
    public KnowledgePoint viewKnowledgePointById(String id) {
        String hourKey = getHourKey(LocalDateTime.now());
        if (!redisTemplate.hasKey(hourKey)) {
            redisTemplate.opsForZSet().add(hourKey, id, 0);
            redisTemplate.expire(hourKey, Duration.ofHours(25));
        }
        redisTemplate.opsForZSet().incrementScore(hourKey, id, 1);
        return getKnowledgePointById(id);
    }

    @Override
    public List<String> get24HourTopKnowledge(int count) {
        Set<ZSetOperations.TypedTuple<String>> set = redisTemplate.opsForZSet()
                .reverseRangeWithScores(SUMMARY_KEY, 0, count - 1);
        if (set == null) return Collections.emptyList();
        List<String> result = new ArrayList<>();
        for (ZSetOperations.TypedTuple<String> item : set) {
            result.add(item.getValue() + " (" + item.getScore().intValue() + ")");
        }
        return result;
    }

    @Override
    public void refreshTop24hRanking() {
        logger.info("start refresh Top 24 hours KG Ranking");
        List<String> keys = new ArrayList<>();
        for (int i = 0; i < 24; i++) {
            keys.add(getHourKey(LocalDateTime.now().minusHours(i)));
        }
        redisTemplate.opsForZSet().unionAndStore(keys.get(0), keys.subList(1, keys.size()), SUMMARY_KEY);
        redisTemplate.expire(SUMMARY_KEY, Duration.ofHours(1));
    }

    private String getHourKey(LocalDateTime time) {
        return KEY_PREFIX + time.format(DateTimeFormatter.ofPattern("yyyyMMddHH"));
    }


}
